#include "i2c.h"
#include <stdio.h>

#define I2C_DEBUG 0

static CommCtrl_t Comm_Flag = C_READY; /* Avoid star flag and stop flag set 1 at the same time */
static __IO uint32_t I2CTimeout;

// Wait for specified I2C event (combination of I2C flags)
// input:
//   I2Cx - I2C port
//   I2C_Event - I2C event (one of I2C_EVENT_XXX values)
// return:
//   I2C_SUCCESS if event happens or I2C_ERROR in case of timeout
// I2C_Status I2Cx_WaitEvent(I2C_Module *I2Cx, uint32_t I2C_Event)
// {
// 	volatile uint32_t wait = I2C_WAIT_TIMEOUT;
// 	uint32_t reg;

// 	while (wait--)
// 	{
// 		reg = I2Cx->SR1;
// 		reg |= I2Cx->SR2 << 16;
// 		reg &= I2C_FLAG_MASK;
// 		if ((reg & I2C_Event) == I2C_Event)
// 			return I2C_SUCCESS;
// 	}

// 	return I2C_ERROR;
// }

// Wait until I2C flag set
// input:
//   I2Cx - I2C port
//   I2C_Flag - I2C flag (one of I2C_F_XXX values)
// return:
//   I2C_SUCCESS if flag set or I2C_ERROR in case of timeout
// I2C_Status I2Cx_WaitFlagSet(I2C_Module *I2Cx, uint32_t I2C_Flag)
// {
// 	volatile uint32_t wait = I2C_WAIT_TIMEOUT;
// 	volatile uint16_t *preg;

// 	// Determine which I2C register to be read
// 	preg = (I2C_Flag & 0x80000000) ? &(I2Cx->SR1) : &(I2Cx->SR2);
// 	I2C_Flag &= 0xFFFF;

// 	// Wait for flag to be set
// 	while (wait--)
// 	{
// 		if (*preg & I2C_Flag)
// 			return I2C_SUCCESS;
// 	}

// 	return I2C_ERROR;
// }

// Wait until I2C flag cleared
// input:
//   I2Cx - I2C port
//   I2C_Flag - I2C flag (one of I2C_F_XXX values)
// return:
//   I2C_SUCCESS if flag cleared or I2C_ERROR in case of timeout
// I2C_Status I2Cx_WaitFlagReset(I2C_Module *I2Cx, uint32_t I2C_Flag)
// {
// 	volatile uint32_t wait = I2C_WAIT_TIMEOUT;
// 	volatile uint16_t *preg;

// 	// Determine which I2C register to be read
// 	preg = (I2C_Flag & 0x80000000) ? &(I2Cx->SR1) : &(I2Cx->SR2);
// 	I2C_Flag &= 0xFFFF;

// 	// Wait until flag cleared
// 	while (wait--)
// 	{
// 		if (!(*preg & I2C_Flag))
// 			return I2C_SUCCESS;
// 	}

// 	return I2C_ERROR;
// }

// Initialize specified I2C peripheral
// input:
//   I2Cx - I2C port
//   Clock - I2C speed (Hz)
// return:
//   I2C_ERROR if there was a timeout during I2C initialization, I2C_SUCCESS otherwise
// note: minimum APB1 frequency for I2C work is 2MHz
// I2C_Status I2Cx_Init(I2C_Module *I2Cx, uint32_t Clock)
// {
// 	I2C_InitType i2c1_master;
// 	// GPIO_InitType i2c1_gpio;

//     GPIO_InitType GPIO_InitStructure;

// 	// RCC_APB1_Peripheral_Clock_Enable(RCC_APB1_PERIPH_I2C1);
// 	// RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_GPIOB);

// 	GPIO_InitStruct(&GPIO_InitStructure);
// 	/*PB6 -- SCL; PB7 -- SDA*/
// 	// i2c1_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
// 	// i2c1_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
// 	// i2c1_gpio.GPIO_Mode = GPIO_MODE_AF_OD;
// 	// i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
// 	// i2c1_gpio.GPIO_Pull = GPIO_PULL_UP;
// 	// GPIO_Peripheral_Initialize(GPIOx, &i2c1_gpio);

//     GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7;
//     GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUTPUT_PP; // 开漏输出
//     GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_HIGH;
//     GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
//     GPIO_InitStructure.GPIO_Alternate = GPIO_AF7_I2C2;
//     GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

// 	I2C_Reset(I2Cx);
// 	I2C_InitStruct(&i2c1_master);

// 	i2c1_master.BusMode = I2C_BUSMODE_I2C;
// 	i2c1_master.DutyCycle = I2C_SMDUTYCYCLE_1;
// 	i2c1_master.OwnAddr1 = I2C_MASTER_ADDR;
// 	i2c1_master.AckEnable = I2C_ACKEN;
// 	i2c1_master.AddrMode = I2C_ADDR_MODE_7BIT;
// 	i2c1_master.ClkSpeed = 100000; /* 100k */

//     I2C_Init(I2Cx, &i2c1_master);

// 	I2C_ON(I2Cx);
// 	return I2C_SUCCESS;
// }

// Send data to I2C port
// input:
//   I2Cx - I2C port
//   buf - pointer to the data buffer
//   nbytes - number of bytes to transmit
//   SlaveAddress - address of slave device
//   stop - generate or not STOP condition (I2C_STOP/I2C_NOSTOP)
// return:
//   I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
I2C_Status I2Cx_Write(I2C_Module *I2Cx, const uint8_t *buf, uint32_t len,
					  uint8_t SlaveAddress, I2C_STOP_TypeDef stop)
{
	uint8_t *sendBufferPtr = (uint8_t *)buf;

	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
	{
		if ((I2CTimeout--) == 0)
		{
			CommTimeOut_CallBack(I2Cx, MASTER_BUSY);
		}
	}
	I2C_ConfigAck(I2Cx, DISABLE);

	if (Comm_Flag == C_READY)
	{
		Comm_Flag = C_START_BIT;
		I2C_GenerateStart(I2Cx, ENABLE);
	}

	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) /* EV5 */
	{
		if ((I2CTimeout--) == 0)
		{
#if I2C_DEBUG
			printf("1 last event: %lX\n", I2C_GetLastEvent(I2Cx));
#endif
			CommTimeOut_CallBack(I2Cx, MASTER_MODE);
		}
	}

	I2C_SendAddr7bit(I2Cx, SlaveAddress, I2C_DIRECTION_SEND);
	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG)) /* EV6 */
	{
		if ((I2CTimeout--) == 0)
		{
#if I2C_DEBUG
			printf("2 last event: %lX\n", I2C_GetLastEvent(I2Cx));
#endif
			CommTimeOut_CallBack(I2Cx, MASTER_TXMODE);
		}
	}
	Comm_Flag = C_READY;

	/* send data */
	while (len-- > 0)
	{
		I2C_SendData(I2Cx, *sendBufferPtr++);
		I2CTimeout = I2CT_LONG_TIMEOUT;
		while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) /* EV8 */
		{
			if ((I2CTimeout--) == 0)
			{
#if I2C_DEBUG
				printf("3 last event: %lX\n", I2C_GetLastEvent(I2Cx));
#endif
				CommTimeOut_CallBack(I2Cx, MASTER_SENDING);
			}
		}
	}

	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED)) /* EV8-2 */
	{
		if ((I2CTimeout--) == 0) {
#if I2C_DEBUG
			printf("4 last event: %lX\n", I2C_GetLastEvent(I2Cx));
#endif
			CommTimeOut_CallBack(I2Cx, MASTER_SENDED);
		}
	}

	I2CTimeout = I2CT_LONG_TIMEOUT;
	if (Comm_Flag == C_READY) {
		Comm_Flag = C_STOP_BIT;
		I2C_GenerateStop(I2Cx, ENABLE);
		while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
			if ((I2CTimeout--) == 0) {
#if I2C_DEBUG
				printf("write stop busy\n");
#endif
				CommTimeOut_CallBack(I2Cx, MASTER_BUSY);
			}
		}
		Comm_Flag = C_READY;
	}
	// printf("send success\n");

	return I2C_SUCCESS;
}

void CommTimeOut_CallBack(I2C_Module *I2Cx, ErrCode_t errcode)
{
#if I2C_DEBUG
	printf("...ErrCode:%d\r\n", errcode);
	printf("sts1: 0x%lX\n", I2C_GetRegister(I2Cx, I2C_REG_STS1));
	printf("sts2: 0x%lX\n", I2C_GetRegister(I2Cx, I2C_REG_STS2));
	printf("ctrl1: 0x%lX\n", I2C_GetRegister(I2Cx, I2C_REG_CTRL1));
	printf("ctrl2: 0x%lX\n", I2C_GetRegister(I2Cx, I2C_REG_CTRL2));
#endif

#if (COMM_RECOVER_MODE == MODULE_SELF_RESET)
	I2C_EnableSoftwareReset(I2Cx, ENABLE);
#elif (COMM_RECOVER_MODE == MODULE_RCC_RESET)
	IIC_RCCReset();
#elif (COMM_RECOVER_MODE == SYSTEM_NVIC_RESET)
	SystemNVICReset();
#endif
}

// Read data from I2C port
// input:
//   I2Cx - I2C port
//   buf - pointer to data buffer
//   nbytes - number of bytes to receive
//   SlaveAddress - address of slave device
// return:
//   I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
I2C_Status I2Cx_Read(I2C_Module *I2Cx, uint8_t *buf, uint32_t len,
					 uint8_t SlaveAddress)
{

	uint8_t *recvBufferPtr = buf;

	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
	{
		if ((I2CTimeout--) == 0)
		{
#if I2C_DEBUG
			printf("read busy\n");
#endif
			CommTimeOut_CallBack(I2Cx, MASTER_BUSY);
		}
	}
	I2C_ConfigAck(I2Cx, ENABLE);

	/* send start */
	if (Comm_Flag == C_READY)
	{
		Comm_Flag = C_START_BIT;
		I2C_GenerateStart(I2Cx, ENABLE);
	}

	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) /* EV5 */
	{
		if ((I2CTimeout--) == 0) {
#if I2C_DEBUG
			printf("read master\n");
#endif
			CommTimeOut_CallBack(I2Cx, MASTER_MODE);
		}
	}
	/* send addr */
	I2C_SendAddr7bit(I2Cx, SlaveAddress, I2C_DIRECTION_RECV);
	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_RXMODE_FLAG)) /* EV6 */
	{
		if ((I2CTimeout--) == 0)
		{
			CommTimeOut_CallBack(I2Cx, MASTER_RXMODE);
		}
	}
	Comm_Flag = C_READY;

	if (len == 1)
	{
		I2C_ConfigAck(I2Cx, DISABLE);
		(void)(I2Cx->STS1); /*/ clear ADDR */
		(void)(I2Cx->STS2);
		if (Comm_Flag == C_READY)
		{
			Comm_Flag = C_STOP_BIT;
			I2C_GenerateStop(I2Cx, ENABLE);
		}

		I2CTimeout = I2CT_LONG_TIMEOUT;
		while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE))
		{
			if ((I2CTimeout--) == 0)
			{
				CommTimeOut_CallBack(I2Cx, MASTER_RECVD);
			}
		}
		*recvBufferPtr++ = I2C_RecvData(I2Cx);
		len--;
	}
	else if (len == 2)
	{
		I2Cx->CTRL1 |= 0x0800; /*/ set ACKPOS */
		(void)(I2Cx->STS1);
		(void)(I2Cx->STS2);
		I2C_ConfigAck(I2Cx, DISABLE);

		I2CTimeout = I2CT_LONG_TIMEOUT;
		while (!I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
		{
			if ((I2CTimeout--) == 0)
			{
				CommTimeOut_CallBack(I2Cx, MASTER_BYTEF);
			}
		}

		if (Comm_Flag == C_READY)
		{
			Comm_Flag = C_STOP_BIT;
			I2C_GenerateStop(I2Cx, ENABLE);
		}

		*recvBufferPtr++ = I2C_RecvData(I2Cx);
		len--;
		*recvBufferPtr++ = I2C_RecvData(I2Cx);
		len--;
	}
	else
	{
		I2C_ConfigAck(I2Cx, ENABLE);
		(void)(I2Cx->STS1);
		(void)(I2Cx->STS2);

		while (len)
		{
			if (len == 3)
			{
				I2CTimeout = I2CT_LONG_TIMEOUT;
				while (!I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
				{
					if ((I2CTimeout--) == 0)
					{
						CommTimeOut_CallBack(I2Cx, MASTER_BYTEF);
					}
				}
				I2C_ConfigAck(I2Cx, DISABLE);
				*recvBufferPtr++ = I2C_RecvData(I2Cx);
				len--;

				I2CTimeout = I2CT_LONG_TIMEOUT;
				while (!I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
				{
					if ((I2CTimeout--) == 0)
					{
						CommTimeOut_CallBack(I2Cx, MASTER_BYTEF);
					}
				}

				if (Comm_Flag == C_READY)
				{
					Comm_Flag = C_STOP_BIT;
					I2C_GenerateStop(I2Cx, ENABLE);
				}

				*recvBufferPtr++ = I2C_RecvData(I2Cx);
				len--;
				*recvBufferPtr++ = I2C_RecvData(I2Cx);
				len--;

				break;
			}

			I2CTimeout = I2CT_LONG_TIMEOUT;
			while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_RECVD_FLAG)) /* EV7 */
			{
				if ((I2CTimeout--) == 0)
				{
					CommTimeOut_CallBack(I2Cx, MASTER_RECVD);
				}
			}
			*recvBufferPtr++ = I2C_RecvData(I2Cx);
			len--;
		}
	}

	I2CTimeout = I2CT_LONG_TIMEOUT;
	while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
	{
		if ((I2CTimeout--) == 0)
		{
			CommTimeOut_CallBack(I2Cx, MASTER_BUSY);
		}
	}
	Comm_Flag = C_READY;

	return I2C_SUCCESS;
}

// Check if target device is ready for communication
// input:
//   I2Cx - I2C port
//   SlaveAddress - address of slave device
//   Trials - number of trials
// return:
//   I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
// I2C_Status I2Cx_IsDeviceReady(I2C_Module* I2Cx, uint8_t SlaveAddress, uint32_t Trials) {
// 	volatile uint32_t wait;
// 	uint16_t reg;

// 	do {
// 		// Initiate a START sequence
// 		I2Cx->CR1 |= I2C_CR1_START;
// 		// Wait for EV5
// 		if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;

// 		// Send the slave address (EV5)
// 		I2Cx->DR = SlaveAddress & ~I2C_OAR1_ADD0; // Last bit be reset (transmitter mode)

// 		// Wait until ADDR or AF bit set
// 		wait = I2C_WAIT_TIMEOUT;
// 		do {
// 			reg = I2Cx->SR1;
// 		} while (!(reg & I2C_SR1_ADDR) && !(reg & I2C_SR1_AF) && --wait);

// 		// Check if device responded
// 		if (reg & I2C_SR1_ADDR) {
// 			// Generate a STOP condition
// 			I2Cx->CR1 |= I2C_CR1_STOP;
// 			// Clear the ADDR flag
// 			(void)I2Cx->SR1;
// 			(void)I2Cx->SR2;
// 			// Wait for a STOP flag
// 			if (I2Cx_WaitFlagReset(I2Cx,I2C_F_STOPF) == I2C_ERROR) return I2C_ERROR;
// 			// Wait until I2C bus is free
// 			if (I2Cx_WaitFlagReset(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;

// 			return I2C_SUCCESS;
// 		} else {
// 			// Generate a STOP condition
// 			I2Cx->CR1 |= I2C_CR1_STOP;
// 			// Clear the AF flag
// 			I2Cx->SR1 &= ~I2C_SR1_AF;
// 			// Wait until I2C bus is free
// 			if (I2Cx_WaitFlagReset(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;
// 		}
// 	} while (--Trials);

// 	return I2C_ERROR;
// }

// I2C_Status I2Cx_IsDeviceReady(I2C_Module *I2Cx, uint8_t SlaveAddress, uint32_t Trials)
// {
// 	volatile uint32_t wait;
// 	uint16_t reg;

// 	do
// 	{
// 		// Initiate a START sequence
// 		I2Cx->CR1 |= I2C_CR1_START;
// 		// Wait for EV5
// 		if (I2Cx_WaitFlagSet(I2Cx, I2C_F_BUSY) == I2C_ERROR)
// 			return I2C_ERROR;

// 		// Send the slave address (EV5)
// 		I2Cx->DR = SlaveAddress & ~I2C_OAR1_ADD0; // Last bit be reset (transmitter mode)

// 		// Wait until ADDR or AF bit set
// 		wait = I2C_WAIT_TIMEOUT;
// 		do
// 		{
// 			reg = I2Cx->SR1;
// 		} while (!(reg & I2C_SR1_ADDR) && !(reg & I2C_SR1_AF) && --wait);

// 		// Check if device responded
// 		if (reg & I2C_SR1_ADDR)
// 		{
// 			// Generate a STOP condition
// 			I2Cx->CR1 |= I2C_CR1_STOP;
// 			// Clear the ADDR flag
// 			(void)I2Cx->SR1;
// 			(void)I2Cx->SR2;
// 			// Wait for a STOP flag
// 			if (I2Cx_WaitFlagReset(I2Cx, I2C_F_STOPF) == I2C_ERROR)
// 				return I2C_ERROR;
// 			// Wait until I2C bus is free
// 			if (I2Cx_WaitFlagReset(I2Cx, I2C_F_BUSY) == I2C_ERROR)
// 				return I2C_ERROR;

// 			return I2C_SUCCESS;
// 		}
// 		else
// 		{
// 			// Generate a STOP condition
// 			I2Cx->CR1 |= I2C_CR1_STOP;
// 			// Clear the AF flag
// 			I2Cx->SR1 &= ~I2C_SR1_AF;
// 			// Wait until I2C bus is free
// 			if (I2Cx_WaitFlagReset(I2Cx, I2C_F_BUSY) == I2C_ERROR)
// 				return I2C_ERROR;
// 		}
// 	} while (--Trials);

// 	return I2C_ERROR;
// }
